﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DirectShowLib;
using DirectShowLib.Sample;
// Add reference C:\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0\Microsoft.DirectX.Direct3D.dll
//using Microsoft.DirectX.Direct3D;
using System.Runtime.InteropServices;
using System.IO;
using DirectShowLib.DES;
using PTCapture.Support;
using System.Drawing.Imaging;

namespace PTCapture.UserControls
{
    public partial class MoviePlayerControl : UserControl, IDisposable
    {
        #region Fields

        /// <summary>
        ///
        /// </summary>
        private const int WMGraphNotify = 0x0400 + 13;
        private const int VolumeFull = 0;
        private const int VolumeSilence = -10000;

        private IGraphBuilder graphBuilder = null;
        private IMediaControl mediaControl = null;
        private IMediaEventEx mediaEventEx = null;
        private IVideoWindow videoWindow = null;
        private IBasicAudio basicAudio = null;

        public IBasicVideo BasicVideo
        {
            get { return basicVideo; }
        }
        private IBasicVideo basicVideo = null;

        public IMediaSeeking MediaSeeking
        {
            get { return mediaSeeking; }
        }
        private IMediaSeeking mediaSeeking = null;

        public IMediaPosition MediaPosition
        {
            get { return mediaPosition; }
        }
        private IMediaPosition mediaPosition = null;
        private IVideoFrameStep frameStep = null;

        public string FileName
        {
            get { return _filename; }
        }
        private string _filename = string.Empty;
        private bool isAudioOnly = false;
        private bool isFullScreen = false;
        private int currentVolume = VolumeFull;

        public PlayState CurrentState
        {
            get { return currentState; }
        }
        private PlayState currentState = PlayState.Stopped;
        private double currentPlaybackRate = 1.0;

        private IntPtr hDrain = IntPtr.Zero;

#if DEBUG
    private DsROTEntry rot = null;
#endif
        //------------------------------------------
        // DXBitmap variables
        //------------------------------------------
        private IBaseFilter vmr9 = null;
        private IVMRMixerBitmap9 mixerBitmap = null;
        private bool mixerEnabled = true;
        private bool usingGDI = true;
        private Color colorKey = Color.Violet; // The color use as ColorKey for GDI operations
        private Bitmap colorKeyBitmap; // A RGB bitmap used for GDI operations.
        private Bitmap alphaBitmap; // A ARGB bitmap used for Direct3D operations
        // Managed Direct3D magic number to retrieve unmanaged Direct3D interfaces
        private const int DxMagicNumber = -759872593;
        // Add reference C:\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0\Microsoft.DirectX.Direct3D.dll
        //private Device device = null; // A Managed Direct3D device
        //private PresentParameters presentParams;
        //private Surface surface = null; // A Direct3D suface filled with alphaBitmap
        private IntPtr unmanagedSurface; // A pointer on the unmanaged surface
        //int w, h;
        //NormalizedRect bitmapRect;
        int sourceWidth, sourceHeight;

        //------------------------------------------
        // BrightnessContrast
        //------------------------------------------
        IVMRFilterConfig9 v;
        IVMRMixerControl9 mixerControl;
        VMR9ProcAmpControlRange range;
        VMR9ProcAmpControl proc;

        #endregion

        #region Initialization

        /// <summary>
        /// Broken out as a seperate control because it would make the MoviePlayerUIControls disappear when running.
        /// Changing the handle/assigning to panel did not fix it.
        /// </summary>
        public MoviePlayerControl()
        {
            InitializeComponent();

            // Get the colorkeyed bitmap without antialiasing
            colorKeyBitmap = BitmapGenerator.GenerateColorKeyBitmap(colorKey, false);
            // Get the bitmap with alpha transparency
            alphaBitmap = BitmapGenerator.GenerateAlphaBitmap();

            // Init Managed Direct3D
            try
            {
                //InitializeDirect3D();
            }
            catch (Exception)
            {
                MessageBox.Show("Could not initialize DirectX. We're not using that right now, so I guess that's ok.", "DirectX Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Get video size and set ratio
        /// </summary>
        /// <param name="nMultiplier"></param>
        /// <param name="nDivider"></param>
        /// <returns></returns>
        private int InitVideoWindow(int nMultiplier, int nDivider)
        {
            int hr = 0;
            int lHeight, lWidth;

            if (this.basicVideo == null)
                return 0;

            // Read the default video size
            hr = this.basicVideo.GetVideoSize(out lWidth, out lHeight);
            if (hr == DsResults.E_NoInterface)
                return 0;

            EnablePlaybackMenu(true, MediaTypePT.Video);

            // Account for requests of normal, half, or double size
            lWidth = lWidth * nMultiplier / nDivider;
            lHeight = lHeight * nMultiplier / nDivider;

            this.ClientSize = new Size(lWidth, lHeight);
            Application.DoEvents();

            hr = this.videoWindow.SetWindowPosition(0, 0, lWidth, lHeight);

            // For bitmap rectangle
            sourceHeight = lHeight;
            sourceWidth = lWidth;

            return hr;
        }

        /// <summary>
        /// No longer need.
        /// </summary>
        /// <returns></returns>
        private int InitPlayerWindow()
        {
            // Reset to a default size for audio and after closing a clip
            //this.ClientSize = new Size(240, 120);

            // Check the 'full size' menu item
            //CheckSizeMenu(menuFileSizeNormal);
            //EnablePlaybackMenu(false, MediaType.Audio);

            return 0;
        }

        /// <summary>
        /// No longer need.
        /// </summary>
        /// <param name="bEnable"></param>
        /// <param name="nMediaType"></param>
        private void EnablePlaybackMenu(bool bEnable, MediaTypePT nMediaType)
        {
            // Enable/disable menu items related to playback (pause, stop, mute)
            //menuFilePause.Enabled = bEnable;
            //menuFileStop.Enabled = bEnable;
            //menuFileMute.Enabled = bEnable;
            //menuRateIncrease.Enabled = bEnable;
            //menuRateDecrease.Enabled = bEnable;
            //menuRateNormal.Enabled = bEnable;
            //menuRateHalf.Enabled = bEnable;
            //menuRateDouble.Enabled = bEnable;

            //// Enable/disable menu items related to video size
            //bool isVideo = (nMediaType == MediaType.Video) ? true : false;

            //menuSingleStep.Enabled = isVideo;
            //menuFileSizeHalf.Enabled = isVideo;
            //menuFileSizeDouble.Enabled = isVideo;
            //menuFileSizeNormal.Enabled = isVideo;
            //menuFileSizeThreeQuarter.Enabled = isVideo;
            //menuFileFullScreen.Enabled = isVideo;
        }

        /// <summary>
        /// No longer need.
        /// </summary>
        /// <param name="item"></param>
        private void CheckSizeMenu(MenuItem item)
        {
            //menuFileSizeHalf.Checked = false;
            //menuFileSizeThreeQuarter.Checked = false;
            //menuFileSizeNormal.Checked = false;
            //menuFileSizeDouble.Checked = false;

            item.Checked = true;
        }

        /// <summary>
        /// Get the rectange for the playback.
        /// </summary>
        /// <returns></returns>
        private NormalizedRect GetDestRectangle()
        {
            //int hr = 0;
            //int width, height, arW, arH;
            NormalizedRect rect = new NormalizedRect();

            // todo get actual video size, look to the dxbitmap sample
            //hr = windowlessCtrl.GetNativeVideoSize(out width, out height, out arW, out arH);
            //DsError.ThrowExceptionForHR(hr);

            // Position the bitmap in the middle of the video stream.
            if (sourceWidth >= sourceHeight)
            {
                rect.top = 0.0f;
                rect.left = (1.0f - ((float)sourceHeight / (float)sourceWidth)) / 2;
                rect.bottom = 1.0f;
                rect.right = rect.left + (float)sourceHeight / (float)sourceWidth;
            }
            else
            {
                rect.top = (1.0f - ((float)sourceWidth / (float)sourceHeight)) / 2;
                rect.left = 0.0f;
                rect.right = rect.top + (float)sourceWidth / (float)sourceHeight;
                rect.bottom = 1.0f;
            }

            return rect;
        }

        /// <summary>
        /// Check visibility.
        /// </summary>
        private void CheckVisibility()
        {
            int hr = 0;
            OABool lVisible;

            if ((this.videoWindow == null) || (this.basicVideo == null))
            {
                // Audio-only files have no video interfaces.  This might also
                // be a file whose video component uses an unknown video codec.
                this.isAudioOnly = true;
                return;
            }
            else
            {
                // Clear the global flag
                this.isAudioOnly = false;
            }

            hr = this.videoWindow.get_Visible(out lVisible);
            if (hr < 0)
            {
                // If this is an audio-only clip, get_Visible() won't work.
                //
                // Also, if this video is encoded with an unsupported codec,
                // we won't see any video, although the audio will work if it is
                // of a supported format.
                if (hr == unchecked((int)0x80004002)) //E_NOINTERFACE
                {
                    this.isAudioOnly = true;
                }
                else
                    DsError.ThrowExceptionForHR(hr);
            }
        }

        /// <summary>
        /// Some video renderers support stepping media frame by frame with the
        /// IVideoFrameStep interface.  See the interface documentation for more
        /// details on frame stepping.
        /// </summary>
        /// <returns></returns>
        private bool GetFrameStepInterface()
        {
            int hr = 0;

            IVideoFrameStep frameStepTest = null;

            // Get the frame step interface, if supported
            frameStepTest = (IVideoFrameStep)this.graphBuilder;

            // Check if this decoder can step
            hr = frameStepTest.CanStep(0, null);
            if (hr == 0)
            {
                this.frameStep = frameStepTest;
                return true;
            }
            else
            {
                // BUG 1560263 found by husakm (thanks)...
                // Marshal.ReleaseComObject(frameStepTest);
                this.frameStep = null;
                return false;
            }
        }

        /// <summary>
        /// No longer need
        /// </summary>
        private void UpdateMainTitle()
        {
            // If no file is loaded, just show the application title
            if (this._filename == string.Empty)
                this.Text = "PlayWnd Media Player";
            else
            {
                string media = (isAudioOnly) ? "Audio" : "Video";
                string muted = (currentVolume == VolumeSilence) ? "Mute" : "";
                string paused = (currentState == PlayState.Paused) ? "Paused" : "";

                this.Text = String.Format("{0} [{1}] {2}{3}", System.IO.Path.GetFileName(this._filename), media, muted, paused);
            }
        }

        /// <summary>
        /// Initialize D3D for bitmap mixing
        /// No longer using
        /// Add reference C:\Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0\Microsoft.DirectX.Direct3D.dll
        /// </summary>
        private void InitializeDirect3D()
        {
            //Device.IsUsingEventHandlers = false;

            //// Basic Presentation Parameters...
            //presentParams = new PresentParameters();
            //presentParams.Windowed = true;
            //presentParams.SwapEffect = SwapEffect.Discard;

            //// Assume a hardware Direct3D device is available
            //// Add MultiThreaded to be safe. Each DirectShow filter runs in a separate thread...
            //device = new Device(
            //    0,
            //    DeviceType.Hardware,
            //    this,
            //    CreateFlags.SoftwareVertexProcessing | CreateFlags.MultiThreaded,
            //    presentParams
            //    );

            //// Create a surface from our alpha bitmap
            //surface = new Surface(device, alphaBitmap, Pool.SystemMemory);
            //// Get the unmanaged pointer
            //unmanagedSurface = surface.GetObjectByValue(DxMagicNumber);
        }

        /// <summary>
        /// Set mixer settings for bitmap mixing
        /// </summary>
        private void SetMixerSettings()
        {
            return;

            int hr = 0;
            VMR9AlphaBitmap alphaBmp;

            if (!mixerEnabled) // Did the user disable the bitmap ?
            {
                // Get current Alpha Bitmap Parameters
                hr = mixerBitmap.GetAlphaBitmapParameters(out alphaBmp);
                DsError.ThrowExceptionForHR(hr);

                // Disable them
                alphaBmp.dwFlags = VMR9AlphaBitmapFlags.Disable;

                // Update the Alpha Bitmap Parameters
                hr = mixerBitmap.UpdateAlphaBitmapParameters(ref alphaBmp);
                DsError.ThrowExceptionForHR(hr);

                return;
            }

            if (usingGDI)
            {
                // Old school GDI stuff...
                Graphics g = Graphics.FromImage(colorKeyBitmap);
                IntPtr hdc = g.GetHdc();
                IntPtr memDC = NativeMethods.CreateCompatibleDC(hdc);
                IntPtr hBitmap = colorKeyBitmap.GetHbitmap();
                NativeMethods.SelectObject(memDC, hBitmap);

                // Set Alpha Bitmap Parameters for using a GDI DC
                alphaBmp = new VMR9AlphaBitmap();
                alphaBmp.dwFlags = VMR9AlphaBitmapFlags.hDC | VMR9AlphaBitmapFlags.SrcColorKey | VMR9AlphaBitmapFlags.FilterMode;
                alphaBmp.hdc = memDC;
                alphaBmp.rSrc = new DsRect(0, 0, colorKeyBitmap.Size.Width, colorKeyBitmap.Size.Height);
                alphaBmp.rDest = GetDestRectangle();
                alphaBmp.clrSrcKey = ColorTranslator.ToWin32(colorKey);
                alphaBmp.dwFilterMode = VMRMixerPrefs.PointFiltering;
                alphaBmp.fAlpha = 0.75f;

                // Set Alpha Bitmap Parameters
                hr = mixerBitmap.SetAlphaBitmap(ref alphaBmp);
                DsError.ThrowExceptionForHR(hr);

                // Release GDI handles
                NativeMethods.DeleteObject(hBitmap);
                NativeMethods.DeleteDC(memDC);
                g.ReleaseHdc(hdc);
                g.Dispose();
            }
            else // Using a Direct3D surface
            {
                // Set Alpha Bitmap Parameters for using a Direct3D surface
                alphaBmp = new VMR9AlphaBitmap();
                alphaBmp.dwFlags = VMR9AlphaBitmapFlags.EntireDDS;
                alphaBmp.pDDS = unmanagedSurface;
                alphaBmp.rDest = GetDestRectangle();
                alphaBmp.fAlpha = 1.0f;
                // Note : Alpha values from the bitmap are cumulative with the fAlpha parameter.
                // Example : texel alpha = 128 (50%) & fAlpha = 0.5f (50%) = effective alpha : 64 (25%)

                // Set Alpha Bitmap Parameters
                hr = mixerBitmap.SetAlphaBitmap(ref alphaBmp);
                DsError.ThrowExceptionForHR(hr);
            }
        }

        #endregion

        #region PlaybackControls

        /// <summary>
        /// Play a clip.
        /// </summary>
        /// <param name="filename"></param>
        public void PlayClip(string filename)
        {
            try
            {
                int hr = 0;

                if (filename == string.Empty)
                    return;

                _filename = filename;
                this.graphBuilder = (IGraphBuilder)new FilterGraph();

                //----------------------------------------
                // BITMAP VARIABLES
                //----------------------------------------
                vmr9 = (IBaseFilter)new VideoMixingRenderer9();

                IVMRFilterConfig9 filterConfig = (IVMRFilterConfig9)vmr9;

                // Not really needed for VMR9 but don't forget calling it with VMR7
                hr = filterConfig.SetNumberOfStreams(1);
                DsError.ThrowExceptionForHR(hr);
                
                hr = graphBuilder.AddFilter(vmr9, "Video Mixing Renderer 9");
                DsError.ThrowExceptionForHR(hr);

                //----------------------------------------
                // END BITMAP VARIABLES
                //----------------------------------------

                // Have the graph builder construct its the appropriate graph automatically
                hr = this.graphBuilder.RenderFile(filename, null);
                DsError.ThrowExceptionForHR(hr);


                //-------------------------------------------
                // BRIGHTNESS TEST
                //-------------------------------------------
                v = (IVMRFilterConfig9)vmr9;
                v.SetNumberOfStreams(1);

                mixerControl = (IVMRMixerControl9)vmr9;
                range = new VMR9ProcAmpControlRange();
                range.dwSize = Marshal.SizeOf(range);
                range.dwProperty = VMR9ProcAmpControlFlags.Brightness;

                proc = new VMR9ProcAmpControl();
                proc.dwSize = Marshal.SizeOf(proc);

                //-------------------------------------------
                // END BRIGHTNESS TEST
                //-------------------------------------------

                // QueryInterface for DirectShow interfaces
                this.mediaControl = (IMediaControl)this.graphBuilder;
                this.mediaEventEx = (IMediaEventEx)this.graphBuilder;
                this.mediaSeeking = (IMediaSeeking)this.graphBuilder;
                this.mediaPosition = (IMediaPosition)this.graphBuilder;

                // Query for video interfaces, which may not be relevant for audio files
                this.videoWindow = this.graphBuilder as IVideoWindow;
                this.basicVideo = this.graphBuilder as IBasicVideo;

                // Query for audio interfaces, which may not be relevant for video-only files
                this.basicAudio = this.graphBuilder as IBasicAudio;

                // Is this an audio-only file (no video component)?
                CheckVisibility();

                // Have the graph signal event via window callbacks for performance
                hr = this.mediaEventEx.SetNotifyWindow(this.Handle, WMGraphNotify, IntPtr.Zero);
                DsError.ThrowExceptionForHR(hr);

                if (!this.isAudioOnly)
                {
                    // Setup the video window
                    hr = this.videoWindow.put_Owner(this.Handle);
                    DsError.ThrowExceptionForHR(hr);

                    hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipSiblings | WindowStyle.ClipChildren);
                    DsError.ThrowExceptionForHR(hr);

                    // TODO Set the multiplier by the client panel window size. Keeps it in frame
                    hr = InitVideoWindow(1, 2);

                    DsError.ThrowExceptionForHR(hr);

                    GetFrameStepInterface();
                }
                else
                {
                    // Initialize the default player size and enable playback menu items
                    hr = InitPlayerWindow();
                    DsError.ThrowExceptionForHR(hr);

                    EnablePlaybackMenu(true, MediaTypePT.Audio);
                }

                //----------------------------------------
                // BITMAP VARIABLES
                //----------------------------------------
                mixerBitmap = (IVMRMixerBitmap9)vmr9;
                SetMixerSettings();
                //----------------------------------------
                // END BITMAP VARIABLES
                //----------------------------------------

                // Complete window initialization
                //CheckSizeMenu(menuFileSizeNormal);
                this.isFullScreen = false;
                this.currentPlaybackRate = 1.0;
                UpdateMainTitle();

#if DEBUG
      rot = new DsROTEntry(this.graphBuilder);
#endif

                this.Focus();

                // Run the graph to play the media file
                hr = this.mediaControl.Run();
                DsError.ThrowExceptionForHR(hr);

                this.currentState = PlayState.Running;
            }
            catch (Exception e)
            {
                //CloseInterfaces();
                // todo break up appropriately
                MessageBox.Show("An error occured during the graph building : \r\n\r\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Pause the clip
        /// </summary>
        public void PauseClip()
        {
            if (this.mediaControl == null)
                return;

            // Toggle play/pause behavior
            if ((this.currentState == PlayState.Paused) || (this.currentState == PlayState.Stopped))
            {
                if (this.mediaControl.Run() >= 0)
                    this.currentState = PlayState.Running;
            }
            else
            {
                if (this.mediaControl.Pause() >= 0)
                    this.currentState = PlayState.Paused;
            }

            UpdateMainTitle();
        }

        /// <summary>
        /// Stop a playing clip.
        /// </summary>
        internal void StopClip()
        {
            int hr = 0;
            DsLong pos = new DsLong(0);

            if ((this.mediaControl == null) || (this.mediaSeeking == null))
                return;

            // Stop and reset postion to beginning
            if ((this.currentState == PlayState.Paused) || (this.currentState == PlayState.Running))
            {
                hr = this.mediaControl.Stop();
                this.currentState = PlayState.Stopped;

                // Seek to the beginning
                hr = this.mediaSeeking.SetPositions(pos, AMSeekingSeekingFlags.AbsolutePositioning, null, AMSeekingSeekingFlags.NoPositioning);

                // Display the first frame to indicate the reset condition
                hr = this.mediaControl.Pause();
            }
            UpdateMainTitle();
        }

        /// <summary>
        /// Step frames if possible.
        /// </summary>
        /// <param name="nFramesToStep"></param>
        /// <returns></returns>
        internal int StepFrames(int nFramesToStep)
        {
            int hr = 0;

            // If the Frame Stepping interface exists, use it to step frames
            if (this.frameStep != null)
            {
                // The renderer may not support frame stepping for more than one
                // frame at a time, so check for support.  S_OK indicates that the
                // renderer can step nFramesToStep successfully.
                hr = this.frameStep.CanStep(nFramesToStep, null);
                if (hr == 0)
                {
                    // The graph must be paused for frame stepping to work
                    if (this.currentState != PlayState.Paused)
                        PauseClip();

                    // Step the requested number of frames, if supported
                    hr = this.frameStep.Step(nFramesToStep, null);
                }
            }

            return hr;
        }

        /// <summary>
        /// Get playing rate.
        /// </summary>
        /// <returns></returns>
        internal double GetRate()
        {
            // If the IMediaPosition interface exists, use it to set rate
            if ((this.mediaPosition != null))
            {
                double dRate = 0.0f;
                this.mediaPosition.get_Rate(out dRate);
                return dRate;
            }

            return -1.0f;
        }

        /// <summary>
        /// Change the rate by input value.
        /// </summary>
        /// <param name="dRateAdjust"></param>
        /// <returns></returns>
        internal int ModifyRate(double dRateAdjust)
        {
            int hr = 0;
            double dRate;

            // If the IMediaPosition interface exists, use it to set rate
            if ((this.mediaPosition != null) && (dRateAdjust != 0.0))
            {
                hr = this.mediaPosition.get_Rate(out dRate);
                if (hr == 0)
                {
                    // Add current rate to adjustment value
                    double dNewRate = dRate + dRateAdjust;
                    hr = this.mediaPosition.put_Rate(dNewRate);

                    // Save global rate
                    if (hr == 0)
                    {
                        this.currentPlaybackRate = dNewRate;
                        UpdateMainTitle();
                    }
                }
            }

            return hr;
        }

        /// <summary>
        /// Set playing rate by value.
        /// </summary>
        /// <param name="rate"></param>
        /// <returns></returns>
        internal int SetRate(double rate)
        {
            int hr = 0;

            // If the IMediaPosition interface exists, use it to set rate
            if (this.mediaPosition != null)
            {
                hr = this.mediaPosition.put_Rate(rate);
                if (hr >= 0)
                {
                    this.currentPlaybackRate = rate;
                    UpdateMainTitle();
                }
            }

            return hr;
        }

        #endregion

        #region Support

        /// <summary>
        /// Get playback ranges.
        /// </summary>
        /// <param name="flag"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <param name="current"></param>
        internal void GetRanges(VMR9ProcAmpControlFlags flag, ref float min, ref float max, ref float current)
        {
            range.dwProperty = flag;

            int hr = 0;

            hr = mixerControl.GetProcAmpControl(0, ref proc);
            DsError.ThrowExceptionForHR(hr);

            hr = mixerControl.GetProcAmpControlRange(0, ref range);
            DsError.ThrowExceptionForHR(hr);

            min = range.MinValue;
            max = range.MaxValue;

            switch (flag)
            {
                case VMR9ProcAmpControlFlags.Brightness:
                    current = proc.Brightness;
                    break;
                case VMR9ProcAmpControlFlags.Contrast:
                    current = proc.Contrast;
                    break;
                default:
                    current = 0;
                    break;
            }
        }

        /// <summary>
        /// Set a playback value.
        /// </summary>
        /// <param name="flag"></param>
        /// <param name="current"></param>
        internal void SetValue(VMR9ProcAmpControlFlags flag, float current)
        {
            int hr = 0;

            range.dwProperty = flag;
            hr = mixerControl.GetProcAmpControlRange(0, ref range);
            DsError.ThrowExceptionForHR(hr);

            switch (flag)
            {
                case VMR9ProcAmpControlFlags.Brightness:
                    if (current > range.MaxValue)
                    {
                        proc.Brightness = range.MaxValue;
                    }
                    else if (current < range.MinValue)
                    {
                        proc.Brightness = range.MinValue;
                    }
                    else
                    {
                        proc.Brightness = current;
                    }
                    break;
                case VMR9ProcAmpControlFlags.Contrast:
                    if (current > range.MaxValue)
                    {
                        proc.Contrast = range.MaxValue;
                    }
                    else if (current < range.MinValue)
                    {
                        proc.Contrast = range.MinValue;
                    }
                    else
                    {
                        proc.Contrast = current;
                    }
                    break;
            }

            hr = mixerControl.SetProcAmpControl(0, proc);
            DsError.ThrowExceptionForHR(hr);
        }

        /// <summary>
        /// Close and release interfaces.
        /// </summary>
        public void CloseInterfaces()
        {
            int hr = 0;

            try
            {
                lock (this)
                {
                    // Relinquish ownership (IMPORTANT!) after hiding video window
                    if (!this.isAudioOnly)
                    {
                        hr = this.videoWindow.put_Visible(OABool.False);
                        DsError.ThrowExceptionForHR(hr);
                        hr = this.videoWindow.put_Owner(IntPtr.Zero);
                        DsError.ThrowExceptionForHR(hr);
                    }

                    if (this.mediaEventEx != null)
                    {
                        hr = this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);
                        DsError.ThrowExceptionForHR(hr);
                    }

#if DEBUG
                    if (rot != null)
                    {
                        rot.Dispose();
                        rot = null;
                    }
#endif
                    // Release and zero DirectShow interfaces
                    if (this.mediaEventEx != null)
                        this.mediaEventEx = null;
                    if (this.mediaSeeking != null)
                        this.mediaSeeking = null;
                    if (this.mediaPosition != null)
                        this.mediaPosition = null;
                    if (this.mediaControl != null)
                        this.mediaControl = null;
                    if (this.basicAudio != null)
                        this.basicAudio = null;
                    if (this.basicVideo != null)
                        this.basicVideo = null;
                    if (this.videoWindow != null)
                        this.videoWindow = null;
                    if (this.frameStep != null)
                        this.frameStep = null;
                    if (this.graphBuilder != null)
                        Marshal.ReleaseComObject(this.graphBuilder); this.graphBuilder = null;

                    GC.Collect();
                }
            }
            catch
            {
            }
        }

        #endregion
    }
}
